Coverage Report

Created: 2021-08-28 18:14

D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\data.h
Line
Count
Source (jump to first uncovered line)
1
//  Copyright 2020-2021 Herald Project Contributors
2
//  SPDX-License-Identifier: Apache-2.0
3
//
4
5
#ifndef HERALD_DATA_H
6
#define HERALD_DATA_H
7
8
#include <string>
9
#include <iostream>
10
11
#include "memory_arena.h"
12
13
namespace herald {
14
namespace datatype {
15
16
/// \brief The main data workhorse class of the Herald API
17
///
18
/// A vitally important part of Herald's datatypes. Many other types
19
/// are actually just aliases or thin wrappers to the DataRef class.
20
/// 
21
/// This class represents an arbitrarily long Big Endian list of std::byte.
22
/// DataRef instances are used to encode Bluetooth advert data, to pass payloads
23
/// between herald enabled devices, or to share data to and from backend systems.
24
#ifndef HERALD_MEMORYARENA_MAX
25
template <typename MemoryArenaT = MemoryArena<8192, 8>>
26
#else
27
#ifndef HERALD_MEMORYARENA_PAGE
28
template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, 8>>
29
#else
30
template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, HERALD_MEMORYARENA_PAGE>>
31
#endif
32
#endif
33
class DataRef {
34
public:
35
  /// \brief Creates a DataRef with no memory used
36
  DataRef() : entry()
37
337
  {
38
337
    ;
39
337
  }
40
  /// \brief Takes control of another DataRef's memory allocation
41
  DataRef(DataRef&& other)
42
  : entry()
43
27
  {
44
27
    std::swap(entry,other.entry);
45
27
  }
46
  /// \brief Initialises a DataRef from a std::uint8_t array of length `length`
47
  DataRef(const std::uint8_t* value, std::size_t length) : 
48
51
  entry(arena.allocate(length)) {
49
525
    for (std::size_t i = 0;i < length; 
++i474
) {
50
474
      // data[i] = std::byte(value[i]);
51
474
      arena.set(entry, i, (unsigned char)value[i]);
52
474
    }
53
51
  }
54
  /// \brief Initialises a DataRef from a std::byte array of length `length`
55
2
  DataRef(const std::byte* value, std::size_t length) : entry(arena.allocate(length)) {
56
10
    for (std::size_t i = 0;i < length; 
++i8
) {
57
8
      // data[i] = value[i];
58
8
      arena.set(entry, i, (unsigned char)value[i]);
59
8
    }
60
2
  }
61
  /// \brief Initialises a DataRef from a string of chars
62
  DataRef(const std::string& from) : entry(arena.allocate(from.size())) {
63
    for (std::size_t i = 0;i < from.size(); ++i) {
64
      // data[i] = value[i];
65
      arena.set(entry, i, (unsigned char)from[i]);
66
    }
67
  }
68
  /// \brief Initialises a DataRef copying another data object (uses more data, to ensure only one object owns the entry)
69
410
  DataRef(const DataRef& from) : entry(arena.allocate(from.entry.byteLength)) {
70
3.11k
    for (std::size_t i = 0;i < from.size(); 
++i2.70k
) {
71
2.70k
      arena.set(entry, i, from.arena.get(from.entry,i));
72
2.70k
    }
73
410
  }
74
75
  /// \brief Initialises a DataRef with count number of repeating bytes
76
102
  DataRef(std::byte repeating, std::size_t count) : entry(arena.allocate(count)) {
77
706
    for (std::size_t i = 0;i < count; 
++i604
) {
78
604
      arena.set(entry,i,(unsigned char)repeating);
79
604
    }
80
102
  }
81
  /// \brief Initialises a DataRef with reserveLength bytes of undefined data
82
201
  DataRef(std::size_t reserveLength) : entry(arena.allocate(reserveLength)) {
83
201
    ;
84
201
  }
85
86
87
  // Data(Base64String from); // use Base64String.from(std::string).decode() instea
88
  /// \brief Copy assign operator. Copies the data to be sure only one object owns the entry
89
  DataRef& operator=(const DataRef& other)
90
35
  {
91
35
    entry = arena.allocate(other.entry.byteLength);
92
487
    for (std::size_t i = 0;i < other.size(); 
++i452
) {
93
452
      arena.set(entry, i, other.arena.get(other.entry,i));
94
452
    }
95
35
    return *this;
96
35
  }
97
98
  /// \brief Default destructor
99
1.09k
  ~DataRef() {
100
1.09k
    clear();
101
1.09k
  }
102
103
  // std::string base64EncodedString(); // use Base64String.encode(Data) instead
104
  /// \brief Creates a new DataRef object from a hexadecimal encoded string
105
  static DataRef fromHexEncodedString(const std::string& hex)
106
138
  {
107
138
    // parse string
108
138
    const std::size_t length = hex.size();
109
138
    std::string hexInput;
110
138
    // Input size check - two characters per single byte
111
138
    if (1 == length % 2) {
112
0
      // invalid format - not an even number of characters
113
0
      // Prepend input with a 0. (Note '8' and '08' in hex are the same)
114
0
      hexInput += "0";
115
0
    }
116
138
    hexInput += hex;
117
138
118
138
    DataRef d(hexInput.size() / 2);
119
138
120
2.32k
    for (std::size_t i = 0; i < hexInput.size(); 
i += 22.18k
) {
121
2.18k
      std::string byteString = hexInput.substr(i, 2);
122
2.18k
      std::byte byte = std::byte(strtol(byteString.c_str(), NULL, 16));
123
2.18k
      // d.data.push_back(byte);
124
2.18k
      arena.set(d.entry,i / 2, (unsigned char)byte);
125
2.18k
    }
126
138
127
138
    return d;
128
138
  }
129
130
  /// \brief Returns the hex encoded string represetation as the description
131
  std::string description() const
132
45
  {
133
45
    return hexEncodedString();
134
45
  }
135
136
  /// \brief Returns a NEWLY allocated DataRef instance returning a subset of this instance
137
  DataRef subdata(std::size_t offset) const
138
4
  {
139
4
    if (offset >= entry.byteLength) {
140
2
      return DataRef(0);
141
2
    }
142
2
    DataRef copy(entry.byteLength - offset);
143
9
    for (std::size_t i = 0;i < entry.byteLength - offset;
++i7
) {
144
7
      copy.arena.set(copy.entry,i,arena.get(entry,i + offset));
145
7
    }
146
2
    // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data));
147
2
    return copy;
148
2
  }
149
150
  /// \brief Returns a NEWLY allocated DataRef instance returning a subset of this instance
151
  DataRef subdata(std::size_t offset, std::size_t length) const
152
26
  {
153
26
    // Note: offset if passed as -1 could be MAX_LONG_LONG, so check it on its own too
154
26
    if (offset >= entry.byteLength) {
155
3
      return DataRef(0);
156
3
    }
157
23
    std::size_t correctedLength = length;
158
23
    if (length > entry.byteLength || 
length + offset > entry.byteLength22
) {
159
4
      correctedLength = entry.byteLength - offset;
160
4
    }
161
23
    DataRef copy(correctedLength);
162
23
    // Note the below is necessary as calling with (4,-1), the second condition IS valid!
163
23
    // if (length > entry.byteLength || offset + length > entry.byteLength) {
164
23
    //   for (std::size_t i = 0;i < entry.byteLength - offset;++i) {
165
23
    //     copy.arena.set(copy.entry,i,arena.get(entry,offset + i));
166
23
    //   }
167
23
    //   // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data));
168
23
    // } else {
169
106
      for (std::size_t i = 0;i < correctedLength;
++i83
) {
170
83
        copy.arena.set(copy.entry,i,arena.get(entry,offset + i));
171
83
      }
172
23
      // std::copy(data.begin() + offset, data.begin() + offset + length, std::back_inserter(copy.data));
173
23
    // }
174
23
    return copy;
175
23
  }
176
177
  /// \brief Returns the individual byte at index position, or a byte value of zero if index is out of bounds.
178
2.57k
  std::byte at(std::size_t index) const {
179
2.57k
    if (index > (unsigned short)(entry.byteLength - 1)) {
180
2
      return std::byte(0);
181
2
    }
182
2.57k
    return std::byte(arena.get(entry,index));
183
2.57k
  }
184
185
  /// \brief 
186
  /// Assigns the data in-place, reserving the size required if 
187
  /// the current size is too small.
188
  ///
189
  /// Avoids repeated reallocation of memory on a copy.
190
  void assign(const DataRef& other)
191
0
  {
192
0
    if (other.size() > entry.byteLength) {
193
0
      arena.reserve(entry,other.size());
194
0
    }
195
0
    for (std::size_t pos = 0; pos < other.size();++pos) {
196
0
      arena.set(entry,pos,other.arena.get(other.entry,pos));
197
0
    }
198
0
  }
199
200
  /// \brief Copies another DataRef into this instance, expanding if required
201
  void append(const DataRef& rawData, std::size_t offset, std::size_t length)
202
  {
203
    auto curSize = entry.byteLength;
204
    arena.reserve(entry,curSize + length);
205
    for (std::size_t pos = 0; pos < length;++pos) {
206
      arena.set(entry,curSize + pos,rawData.arena.get(rawData.entry,pos + offset));
207
    }
208
    // std::copy(rawData.data.begin() + offset, 
209
    //           rawData.data.begin() + offset + length, 
210
    //           std::back_inserter(data)
211
    // );
212
  }
213
214
  /// \brief Appends a set of characters to the end of this DataRef
215
  void append(const std::string& rawData)
216
4
  {
217
4
    auto curSize = entry.byteLength;
218
4
    arena.reserve(entry,curSize + rawData.size());
219
74
    for (std::size_t pos = 0; pos < rawData.size();
++pos70
) {
220
70
      arena.set(entry,curSize + pos,rawData[pos]);
221
70
    }
222
4
  }
223
224
  /// \brief Copies a uint8_t array onto the end of this instance, expanding if necessary
225
  void append(const std::uint8_t* rawData, std::size_t offset, std::size_t length)
226
24
  {
227
24
    auto curSize = entry.byteLength;
228
24
    arena.reserve(entry,curSize + length);
229
170
    for (std::size_t i = 0;i < length;
++i146
) {
230
146
      arena.set(entry,curSize + i,(unsigned char)(rawData[offset + i]));
231
146
      // arena.set(entry,curSize,std::byte(rawData[offset + i]));
232
146
    }
233
24
  }
234
235
  /// \brief Appends the specified DataRef to this one, but in its reverse order
236
  void appendReversed(const DataRef& rawData, std::size_t offset, std::size_t length)
237
6
  {
238
6
    if (offset > rawData.size()) {
239
0
      return; // append nothing - out of range
240
0
    }
241
6
    std::size_t checkedLength = length;
242
6
    if (length > (rawData.size() - offset)) {
243
4
      checkedLength = rawData.size() - offset;
244
4
    }
245
6
    auto curSize = entry.byteLength;
246
6
    arena.reserve(entry,curSize + checkedLength);
247
24
    for (std::size_t i = 0;i < checkedLength;
++i18
) {
248
18
      arena.set(entry,curSize + i,
249
18
      rawData.arena.get(rawData.entry,offset + (checkedLength - i - 1)));
250
18
    // std::reverse_copy(rawData.data.begin() + offset, 
251
18
    //                   rawData.data.begin() + offset + checkedLength, 
252
18
    //                   std::back_inserter(data)
253
18
    // );
254
18
    }
255
6
  }
256
257
  /// \brief Appends the specified DataRef to this one
258
  void append(const DataRef& rawData)
259
26
  {
260
26
    auto orig = entry.byteLength;
261
26
    arena.reserve(entry,rawData.size() + orig);
262
204
    for (std::size_t pos = 0; pos < rawData.size();
++pos178
) {
263
178
      arena.set(entry,orig + pos,rawData.arena.get(rawData.entry,pos));
264
178
    }
265
26
    // std::copy(rawData.data.begin(), rawData.data.end(), std::back_inserter(data));
266
26
  }
267
268
  /// \brief Appends a single byte
269
  void append(std::byte rawData)
270
176
  {
271
176
    std::size_t curSize = entry.byteLength;
272
176
    arena.reserve(entry,curSize + 1);
273
176
    // data.push_back(rawData);
274
176
    arena.set(entry,curSize,(unsigned char)rawData);
275
176
    // curSize++;
276
176
  }
277
278
  /// \brief appends a single uint8_t
279
  void append(uint8_t rawData)
280
11
  {
281
11
    std::size_t curSize = entry.byteLength;
282
11
    arena.reserve(entry,curSize + 1); // C++ ensures types are AT LEAST x bits
283
11
    // arena.set(entry,curSize,std::byte(rawData));
284
11
    arena.set(entry,curSize,(unsigned char)(rawData));
285
11
    // curSize++;
286
11
  }
287
288
  /// \brief Appends a single uint16_t
289
  void append(uint16_t rawData)
290
9
  {
291
9
    std::size_t curSize = entry.byteLength;
292
9
    arena.reserve(entry,curSize + 2); // C++ ensures types are AT LEAST x bits
293
9
    arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
294
9
    arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
295
9
  }
296
297
  /// \brief Appends a single uint32_t
298
  void append(uint32_t rawData)
299
3
  {
300
3
    std::size_t curSize = entry.byteLength;
301
3
    arena.reserve(entry,curSize + 4); // C++ ensures types are AT LEAST x bits
302
3
    arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
303
3
    arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
304
3
    arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16));
305
3
    arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24));
306
3
  }
307
308
  /// \brief Appends a single uint64_t
309
  void append(uint64_t rawData)
310
3
  {
311
3
    std::size_t curSize = entry.byteLength;
312
3
    arena.reserve(entry,curSize + 8); // C++ ensures types are AT LEAST x bits
313
3
    arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
314
3
    arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
315
3
    arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16));
316
3
    arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24));
317
3
    arena.set(entry,curSize + 4,(unsigned char)(rawData >> 32));
318
3
    arena.set(entry,curSize + 5,(unsigned char)(rawData >> 40));
319
3
    arena.set(entry,curSize + 6,(unsigned char)(rawData >> 48));
320
3
    arena.set(entry,curSize + 7,(unsigned char)(rawData >> 56));
321
3
  }
322
323
  /// \brief Returns whether reading a single uint8_t to `into` at `fromIndex` was successful
324
  bool uint8(std::size_t fromIndex, uint8_t& into) const noexcept
325
1.56k
  {
326
1.56k
    if (fromIndex > (unsigned short)(entry.byteLength - 1)) {
327
0
      return false;
328
0
    }
329
1.56k
    into = std::uint8_t(arena.get(entry,fromIndex));
330
1.56k
    return true;
331
1.56k
  }
332
333
  /// \brief Returns whether reading a single uint16_t to `into` at `fromIndex` was successful
334
  bool uint16(std::size_t fromIndex, uint16_t& into) const noexcept
335
7
  {
336
7
    if (fromIndex > (unsigned short)(entry.byteLength - 2)) {
337
0
      return false;
338
0
    }
339
7
    into = (std::uint16_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | std::uint16_t(std::uint8_t(arena.get(entry,fromIndex)));
340
7
    return true;
341
7
  }
342
343
  /// \brief Returns whether reading a single uint32_t to `into` at `fromIndex` was successful
344
  bool uint32(std::size_t fromIndex, uint32_t& into) const noexcept
345
2
  {
346
2
    if (fromIndex > entry.byteLength - 4) {
347
0
      return false;
348
0
    }
349
2
    into =  std::uint32_t(std::uint8_t(arena.get(entry,fromIndex)))            | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) |
350
2
          (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24);
351
2
    return true;
352
2
  }
353
354
  /// \brief Returns whether reading a single uint64_t to `into` at `fromIndex` was successful
355
  bool uint64(std::size_t fromIndex, uint64_t& into) const noexcept
356
3
  {
357
3
    if (entry.byteLength < 8 || fromIndex > entry.byteLength - 8) {
358
0
      return false;
359
0
    }
360
3
    into = (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 7))) << 56) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 6))) << 48) |
361
3
          (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 5))) << 40) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 4))) << 32) |
362
3
          (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) |
363
3
          (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8)  |  std::uint64_t(std::uint8_t(arena.get(entry,fromIndex)));
364
3
    return true;
365
3
  }
366
367
  // TODO signed versions of the above functions too
368
  /// \brief Equality operator for another DataRef instance (same memory arena)
369
  bool operator==(const DataRef& other) const noexcept
370
23
  {
371
23
    if (size() != other.size()) {
372
0
      return false;
373
0
    }
374
23
    //if (hashCode() != other.hashCode()) {
375
23
    //  return false;
376
23
    //}
377
23
    // else compare each value
378
23
379
23
    // alternatively, cheat...
380
23
    return hashCode() == other.hashCode(); // Somewhat naughty
381
23
  }
382
383
  /// \brief Inequality operator for another DataRef instance (same memory arena)
384
  bool operator!=(const DataRef& other) const noexcept
385
8
  {
386
8
    if (size() != other.size()) {
387
0
      return true;
388
0
    }
389
8
    return hashCode() != other.hashCode();
390
8
  }
391
392
  /// \brief Less than operator for another DataRef instance (same memory arena)
393
  bool operator<(const DataRef& other) const noexcept
394
4
  {
395
4
    return hashCode() < other.hashCode();
396
4
  }
397
398
  /// \brief Greater than operator for another DataRef instance (same memory arena)
399
  bool operator>(const DataRef& other) const noexcept
400
0
  {
401
0
    return hashCode() > other.hashCode();
402
0
  }
403
404
  /// \brief Returns a new DataRef instance with the same data as this one, but in the reverse order
405
  DataRef reversed() const
406
32
  {
407
32
    DataRef result(entry.byteLength);
408
32
    // result.reserve(entry.byteLength);
409
216
    for (std::size_t pos = 0;pos < entry.byteLength;
++pos184
) {
410
184
      result.arena.set(result.entry,pos,arena.get(entry,entry.byteLength - pos - 1));
411
184
    }
412
32
    // std::reverse_copy(data.begin(),data.end(),
413
32
    //   std::back_inserter(result.data)
414
32
    // );
415
32
    return result;
416
32
  }
417
418
  /// \brief Returns the same order of bytes, but with the bits in each byte reversed
419
  DataRef reverseEndianness() const
420
1
  {
421
1
    DataRef result(entry.byteLength);
422
1
    // result.data.reserve(entry.byteLength);
423
1
424
1
    // Keep byte order intact (caller could use reversed() to change that)
425
1
    // but reverse the order of the individual bits by each byte
426
1
    std::uint8_t value, original;
427
6
    for (std::size_t i = 0;i < entry.byteLength;
++i5
) {
428
5
      original = std::uint8_t(arena.get(entry,i));
429
5
      value = 0;
430
45
      for (int b = 0;b < 8;
++b40
) {
431
40
        if ((original & (1 << b)) > 0) {
432
20
          value |= 1 << (7 - b);
433
20
        }
434
40
      }
435
5
      // result.data[i] = std::byte(value);
436
5
      result.arena.set(result.entry,entry.byteLength - i - 1,value);
437
5
    }
438
1
439
1
    return result;
440
1
  }
441
442
  /// \brief Returns a hex encoded string of this binary data
443
  std::string hexEncodedString() const noexcept
444
95
  {
445
95
    if (0 == entry.byteLength) {
446
4
      return "";
447
4
    }
448
91
    std::string result;
449
91
    std::size_t size = entry.byteLength;
450
91
    result.reserve(size * 2);
451
91
    std::size_t v;
452
742
    for (std::size_t i = 0; i < size; 
++i651
) {
453
651
      // v = std::size_t(data.at(i));
454
651
      v = std::size_t(arena.get(entry,i));
455
651
      result += hexChars[0x0F & (v >> 4)]; // MSB
456
651
      result += hexChars[0x0F &  v      ]; // LSB
457
651
    }
458
91
    return result;
459
91
  }
460
461
  /// \brief Returns the hash code of this instance
462
  std::size_t hashCode() const noexcept
463
86
  {
464
86
    // TODO consider a faster (E.g. SIMD) algorithm or one with less hotspots (see hashdos attacks)
465
86
    return std::hash<DataRef<MemoryArenaT>>{}(*this);
466
86
  }
467
468
  /// \brief Returns the size in allocated bytes of this instance
469
6.03k
  std::size_t size() const noexcept{
470
6.03k
    return entry.byteLength;
471
6.03k
  }
472
  // TODO support other C++ STD container type functions to allow iteration over data elements (uint8)
473
474
  /// \brief Clears (deallocates) the bytes referred to by this instance
475
  void clear() noexcept
476
1.11k
  {
477
1.11k
    arena.deallocate(entry);
478
1.11k
  }
479
480
  static MemoryArenaT& getArena() {
481
    return arena;
482
  }
483
  
484
protected:
485
  static const char hexChars[];
486
  static MemoryArenaT arena;
487
  MemoryArenaEntry entry;
488
};
489
490
491
492
493
/// \brief Instantiates the MemoryArena instance used by all DataRefs that share it
494
template <typename MemoryArenaT>
495
MemoryArenaT DataRef<MemoryArenaT>::arena = MemoryArenaT();
496
497
template <typename MemoryArenaT>
498
const char DataRef<MemoryArenaT>::hexChars[] = {
499
  '0','1','2','3','4','5','6','7',
500
  '8','9','a','b','c','d','e','f'
501
};
502
503
/// \brief Defaults references to Data to equal the DataRef with the default Memory Arena dimensions, unless HERALD_MEMORYARENA_MAX is specified
504
/// May also have its allocation size set by HERALD_MEMORYARENA_PAGE. Note this only takes
505
/// affect if HERALD_MEMORYARENA_MAX is also specified.
506
using Data = DataRef<>; // uses MemoryArenaT<4096,8>
507
508
509
510
511
/// \brief Represents a fixed array of Data references using the default memory arena that tracks its own in-use size
512
template <std::size_t maxSize = 8>
513
class DataSections {
514
public:
515
  static constexpr std::size_t MaxSize = maxSize;
516
517
  /// \brief Default constructors. No memory allocation other than size (8 bytes)
518
  DataSections() noexcept : sections(), sz(0) {}
519
  /// \brief Default destructor
520
  ~DataSections() noexcept = default;
521
522
  /// \brief Adds a new section, if room exists. Otherwise quietly performs a NO OP.
523
  void append(const Data& toCopy) noexcept {
524
    if (sz >= MaxSize) {
525
      return;
526
    }
527
    sections[sz] = toCopy; // copy assign operator
528
    ++sz;
529
  }
530
531
  /// \brief Returns the number of dataref elements in use
532
  std::size_t size() noexcept {
533
    return sz;
534
  }
535
536
  /// \brief Returns the DataRef at the relevant index, or an empty DataRef
537
  const Data& get(std::size_t index) noexcept {
538
    if (index >= MaxSize) {
539
      return emptyRef;
540
    }
541
    return sections[index];
542
  }
543
544
  /// \brief Returns the DataRef at the relevant index, or an empty DataRef
545
  const Data& operator[](std::size_t index) noexcept {
546
    return get(index);
547
  }
548
549
private:
550
  static Data emptyRef;
551
  std::array<Data,MaxSize> sections;
552
  std::size_t sz;
553
};
554
555
/// \brief Reference to empty data item for optional/empty return by ref
556
template <std::size_t maxSize>
557
Data DataSections<maxSize>::emptyRef = Data();
558
559
} // end namespace
560
} // end namespace
561
562
namespace std {
563
  template <typename MemoryArenaT>
564
  inline std::ostream& operator<<(std::ostream &os, const herald::datatype::DataRef<MemoryArenaT>& d)
565
1
  {
566
1
    return os << d.hexEncodedString();
567
1
  }
568
569
  inline void hash_combine_impl(std::size_t& seed, std::size_t value)
570
1.46k
  {
571
1.46k
    seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
572
1.46k
  }
573
574
  template<typename MemoryArenaT>
575
  struct hash<herald::datatype::DataRef<MemoryArenaT>>
576
  {
577
    size_t operator()(const herald::datatype::DataRef<MemoryArenaT>& v) const
578
244
    {
579
244
      std::size_t hv = 0;
580
244
      std::uint8_t ui = 0;
581
244
      bool ok;
582
1.71k
      for (std::size_t pos = 0;pos < v.size();
++pos1.46k
) {
583
1.46k
        ok = v.uint8(pos,ui);
584
1.46k
        hash_combine_impl(hv, std::hash<std::uint8_t>()(ui));
585
1.46k
      }
586
244
      return hv;
587
244
    }
588
  };
589
} // end namespace
590
591
#endif